package org.datavis.axis;

import java.text.NumberFormat;
import java.util.ArrayList;
import org.datavis.chart.Chart;
import org.datavis.data.Data;
import org.datavis.util.ChartUtil;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DrawFilter;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Rect;
import android.view.View;

/**
 * @author Benjamin Vogler
 * @version 1.0
 * 
 * VerticalAxis is used to draw the vertical axis in charts
 *  
 */
public class VerticalAxis extends View {
	
	protected Chart chart = null;
	protected Canvas canvas = null;
	protected Bitmap bitmap = null;
	protected int origin = 0;
	protected Data data = null;
	protected int decimals = 0;
	protected ArrayList<Double> ticks = new ArrayList<Double>();
	protected double tickMarkRange = 0.;
	protected float fontSize = 0.f;
	protected Paint mPaint = null;
	protected NumberFormat numberFormatter = null;
	

	public VerticalAxis(Context context, Chart chart) {
		super(context);
		this.chart = chart;
		// TODO Auto-generated constructor stub
	}
	
	 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		 if (bitmap != null) {
			 bitmap .recycle();
		 }
		 if(w > 0 && h > 0)
		 {
			 canvas = new Canvas();
			 DrawFilter drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG);
			 canvas.setDrawFilter(drawFilter);
			 bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
			 canvas.setBitmap(bitmap);
		 }
	 }
	 
	 public void destroy() {
		 if (bitmap != null) {
			 bitmap.recycle();
		 }
	 }
	
	 @Override
	 public void onDraw(Canvas c) {
		 
		super.onDraw(c);
		canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
		
		numberFormatter = NumberFormat.getInstance();
		numberFormatter.setMaximumFractionDigits(Integer.MAX_VALUE);
		
		mPaint = new Paint(); 
		mPaint.setColor(0xFF000000);  
		mPaint.setStyle(Paint.Style.FILL);
		mPaint.setStrokeWidth(0.5f);
		mPaint.setTypeface(chart.getFontType());

		data = chart.getData();
		fontSize = chart.getFontSize();
			
	 	if(data != null)
	 	{
	 		//calculate origin
	 		origin = calculateOrigin();
	 		
	 		//draw tickmarks
	 		if(chart.isTickMarks())
	 		{
	 			//calculate tickmarks
	 			tickMarkRange = chart.getTickMarkRange();
	 			if(tickMarkRange == 0.)
	 				tickMarkRange = calculateStandardTickMarkRange(data.getValueRange());
	 			ticks = calculateTickMarks(tickMarkRange);
	 			
//	 			//calculate tick font size
//	 			mPaint.setTextSize(1.f);
//		 		if(chart.getvAxisFontSize() == 0.f)
//		 			tickFontSize = calculateTickFontSize();
//		 		else
//		 			tickFontSize = chart.getvAxisFontSize();
	 			
		 		//draw tickmarks
		 		drawTickMarks();
	 		}
	 		
	 		//draw title
	 		if(chart.getvAxisTitle() != "")
	 		{
//	 			//calculate title fontSize
//	 			titleFontSize = calculateTitleFontSize();
	 			//draw title
	 			drawTitle();
	 		}
	 	} 		 	
	 	
	 	//draw Axis
	 	mPaint.setStrokeWidth(0.1f);
	 	canvas.drawLine(this.getWidth(), 0,this.getWidth() , this.getHeight(), mPaint);
	 		 	
	 	c.drawBitmap(bitmap, 
			new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()), 
			new Rect(0,0,bitmap.getWidth(),bitmap.getHeight()), null);
	}

	//draw tick marks
	protected void drawTickMarks() {
	 	int tickHeight = 0;
	 	mPaint.setTextSize(fontSize);
	 	mPaint.setTextAlign(Paint.Align.RIGHT);
		mPaint.setStrokeWidth(0.1f);		
	 	
	 	for (double item : ticks)
	 	{
	 		if (item < 0.)
	 			tickHeight = origin + calculateHeight(data.getValueRange(), item);
			else
				tickHeight = origin - calculateHeight(data.getValueRange(), item);
	 		canvas.drawLine(this.getWidth()- 3, tickHeight, this.getWidth(), tickHeight, mPaint);
	 		canvas.drawText(numberFormatter.format(item), this.getWidth() - 5, tickHeight +(mPaint.getTextSize()/3), mPaint);
	 	}	
	}
	
	//draw title
	protected void drawTitle() {	
		canvas.save();	      
        float py = this.getHeight()/2.0f;
        canvas.rotate(-90, 0, 0);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTextSize(fontSize);
        canvas.drawText(chart.getvAxisTitle(), -py, fontSize, mPaint);
        
        canvas.restore(); 
	}

	//calculate origin position
	protected int calculateOrigin() {
	 	int o = 0;
	 	
	 	if(data.getMaxValue()> 0. && (data.getMinValue() < 0.))
		{
			o = (int)(this.getHeight() * (data.getMaxValue()/data.getValueRange()));
		}
		else if (data.getMaxValue() > 0.)
		{
			o = this.getHeight();
		}
		else if (data.getMaxValue() < 0)
		{
			o = 0;
		}
	 	
	 	return o;
	}
	
	//calculate standard tickmark range
	protected double calculateStandardTickMarkRange(double valueRange) {
		double tmRange = 0.;
		double standardTmRange = fontSize;
		int valueRangeDecimals = ChartUtil.calculateDecimals(valueRange);
				
		double approxTickAmount = this.getHeight()/standardTmRange;
				
		double normalizedRange = valueRange * Math.pow(10, -((double)(valueRangeDecimals)));
		
		double approxTickmarkHeight = normalizedRange/approxTickAmount;
		
		if(approxTickmarkHeight < 0.1)
		{
			tmRange = .1 * Math.pow(10, ((double)(valueRangeDecimals)));
		}
		else if(approxTickmarkHeight > 0.1 && approxTickmarkHeight < 0.25)
		{
			tmRange = .25 * Math.pow(10, ((double)(valueRangeDecimals)));
		}
		else if(approxTickmarkHeight >= 0.25 && approxTickmarkHeight < 0.5)
		{
			tmRange = .5 * Math.pow(10, ((double)(valueRangeDecimals)));
		}
		else
		{
			tmRange = 1 * Math.pow(10, ((double)(valueRangeDecimals)));
		}
		
		return tmRange;
	}
	
	//calculate tick marks
	protected ArrayList<Double> calculateTickMarks(double tmRange) {
		ArrayList<Double> t = new ArrayList<Double>();
		
		double tick = tmRange; 	
	 	
	 	if(data.getMaxValue() > 0 && data.getMinValue() <0)
	 		t.add(0.);
	 	
	 	while (tick < (data.getMaxValue() -(tmRange/3)))
	 	{
	 		t.add(tick);
	 		tick += tmRange;	 	
	 	}
	 	
	 	tick = -tmRange;
	 	
	 	while (tick > (data.getMinValue() + (tmRange/3)))
	 	{
	 		t.add(tick);
	 		tick -= tmRange;	 	
	 	}
		
		return t;
	}
	
	//calculates font size of tickamrks based on width (not greater than 12 or smaller than 6)
	protected float calculateTickFontSize() {
		float fontSize = 1.f;
		float horizontalMax = (float)(this.getHeight() / ticks.size());
		float verticalMax = 1.f;
		String valueToMeasure = numberFormatter.format(data.getValueRange());
		float verticalMaxWidth = 0.f;
				
		if(chart.getvAxisTitle() != "")
			verticalMaxWidth = (this.getWidth()-10)/2;
		else
			verticalMaxWidth = this.getWidth()-10;
		
		float textSize = mPaint.measureText(valueToMeasure);
		
		while(textSize < verticalMaxWidth && verticalMax < horizontalMax)
		{
			verticalMax++;
			mPaint.setTextSize(verticalMax);
			textSize = mPaint.measureText(valueToMeasure);
		}
		
		fontSize = Math.min(verticalMax, horizontalMax);
		
		if (fontSize > 12)
			fontSize = 12.f;
		if (fontSize < 5)
			fontSize = 6.f;
		
		return fontSize;
	}
	
	//calculates title font size based on width
	protected float calculateTitleFontSize() {
		float fontSize = 1.f;
		float horizontalMax = 1.f;
		float verticalMax = 1.f;		
		String valueToMeasure = chart.getvAxisTitle();
		float horizontalMaxWidth = this.getHeight();
		
		if(chart.isTickMarks())
			verticalMax = (this.getWidth()-2)/2;
		else
			verticalMax = this.getWidth()-2;
		
		float textSize = mPaint.measureText(valueToMeasure);
		
		while(textSize < horizontalMaxWidth && horizontalMax < verticalMax)
		{
			horizontalMax++;
			mPaint.setTextSize(horizontalMax);
			textSize = mPaint.measureText(valueToMeasure);
		}
		
		fontSize = Math.min(verticalMax, horizontalMax);
		
		if (fontSize > 12.f)
			fontSize = 12.f;
		if (fontSize < 5.f)
			fontSize = 6.f;
		
		return fontSize;
		
	}
 
	 protected int calculateHeight(double maxValue, double value){
	 	int height = 0;    	    	    	
	 	
	 	if(value < 0.)
	 		value = -value;
	 	 
	 	height = (int)(this.getHeight() * (value/maxValue));
	 	
	 	return height;
	 }
	 


}
